package artificialIntelligence;

import java.util.ArrayList;

import Game2048.Game2048Model;
import model.State;
import view.Board;

/**
 * This class contains the logic of the AI algorithm for auto-solving the game.<br>
 * Default Ctor gets {@link Board}
 * @see State
 * @author Barak & Tom
 */
public class AutoSolver
{
	private Game2048Model game = new Game2048Model();
	public int move = 0,dep;

	
	
	
	/**
	 * Calculating the next most valuable State using the MiniMax AI Algorithm
	 * @param node for current State
	 * @param depth for desired calculated depth
	 * @param a
	 * @param b
	 * @param maximizingPlayer
	 * @return Most valuable State (int)
	 * @see State
	 */
	public int minimax(State node,int depth,int a,int b,Boolean maximizingPlayer){
	    if (depth == 0 ||win(node)){
	        return heuristicScore(node.score, numberOfEmptyCells(node), clusteringScore(node));
	    }
	    game.currentState = node;
	    if (maximizingPlayer){
	    	int i = -1,retVal;
	    	for (State child :playerMoves(node)) {
	    		i++;
	    		if (child == null) {
					continue;																														
				}
	    		
	    		retVal = minimax(child, depth-1, a, b, false);
	            //a = Math.max(a, movecost);
	    		if (retVal>a) {
	    			a=retVal;
	    			if (depth == dep) {
						move = i;
					}
	    			
					
				}
	            if (b <= a){
	                break; // b cut-off
	            }
	            
	    	}
	       	 
	    	 return a;
	    	
	    }
	    else{
		        for (State child :comMoves(node)) {
			        if (child != null) {
		        		b = Math.min(b, minimax(child, depth, a, b, true));
			            if (b <= a){
			                break;// a cut-off 
			            }
			        }
		        }
		        return b;   
		}
	 }
	        
	private int numberOfEmptyCells(State node) {
		int empty = 0;
		for (int[] i : node.boardState) {
			for (int j : i) {
				if (j == 0) {
					empty+=1;
				}
			}
		}
		return empty;
	}

	private int clusteringScore(State node) {
		int absSum = 0;
		for (int i = 0; i < node.boardState.length; i++) {
			for (int j = 0; j < node.boardState[i].length; j++) {
				if (node.boardState[i][j]!=0) {
					for (int y = i-1; y < i+2; y++) {
						if (y<4 && y>-1) {
							for (int x = j-1; x < j+2; x++) {
								if (x<4 && x>-1 && node.boardState[y][x] != 0) {
									absSum += Math.abs(node.boardState[i][j]-node.boardState[y][x]);
								}
							}
							
						}
						
					}
					
				}
				
			}
			
		}
		return absSum;
	}

	private State[] playerMoves(State node) {
		return new State[]{game.moveDown(node),game.moveLeft(node),game.moveRight(node),game.moveUp(node)};
	}

	
	
	    private State[] comMoves(State node) {
	    	ArrayList<State> moves =new  ArrayList<State>();
	    	for (int i = 0; i < node.boardState.length; i++) {
	    		for (int j = 0; j < node.boardState[i].length; j++) {
	    			if (node.boardState[i][j] == 0) {
	    				State s2 = new State(node.stateId, node.score, game.cloneBoard(node.boardState), node),
	    					  s4 = new State(node.stateId, node.score, game.cloneBoard(node.boardState), node);
						s4.boardState[i][j] = 4;
						s2.boardState[i][j] = 2;
						moves.add(s4);
						moves.add(s2);
					}
					
				}
				
			}
		// TODO Auto-generated method stub
	    State[] a = new State[moves.size()];
		return moves.toArray(a);
	}


	private int heuristicScore(int actualScore, int numberOfEmptyCells, int clusteringScore) {
	     int score = (int) (actualScore+Math.log(actualScore)*numberOfEmptyCells -clusteringScore);
	     return Math.max(score, Math.min(actualScore, 1));
	}

	/**
	 * Check if player reached winning goal
	 * @param node for current player's State
	 * @return True if player win the game
	 * @see State
	 */
	private boolean win(State node) {
		for (int[] i : node.boardState) {
			for (int j : i) {
				if (j == 2048) {
					return true;
				}
			}
			
		}
		return false;
	}
	
public void setDept(int dept){this.dep = dept;}
}
